<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>店铺后台管理</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 24px; }
    h2 { margin-top: 0; }
    table { width: 100%; border-collapse: collapse; margin-top: 12px; }
    th, td { border: 1px solid #ddd; padding: 8px; }
    th { background: #f5f5f5; }
    form { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 12px; }
    form input { padding: 6px; }
    .actions { display: flex; gap: 8px; }
    .toolbar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
    .toolbar input, .toolbar select { padding: 6px; }
    .btn { padding: 6px 12px; cursor: pointer; border: 1px solid #333; background: #fff; }
    .btn.primary { background: #1677ff; color: #fff; border-color: #1677ff; }
    .muted { color: #999; }
    .pagination { display: flex; gap: 8px; align-items: center; margin-top: 12px; }
    .err { color: #d93025; font-size: 12px; grid-column: 1 / -1; }
    #map { height: 360px; width: 100%; margin-top: 8px; border: 1px solid #ddd; }
    .section { margin-top: 24px; }
  </style>
  <script src="https://webapi.amap.com/maps?v=2.0&key=YOUR_AMAP_KEY&plugin=AMap.Autocomplete,AMap.PlaceSearch,AMap.Geocoder"></script>
</head>
<body>
  <h2>店铺管理</h2>

  <div class="toolbar">
    <label>品牌：</label>
    <input id="brandFilter" placeholder="如 kfc（留空为全部）" />
    <label>城市关键词：</label>
    <input id="cityFilter" placeholder="如 北京（前端过滤，基于地址包含）" />
    <label>每页：</label>
    <select id="pageSize" onchange="renderTable()">
      <option>5</option><option selected>10</option><option>20</option>
    </select>
    <button class="btn" onclick="loadShops()">加载</button>
    <span class="muted">新增/编辑会自动同步 Redis GEO；导航使用高德</span>
  </div>

  <table>
    <thead>
      <tr>
        <th>ID</th><th>名称</th><th>品牌</th><th>地址</th><th>经度</th><th>纬度</th><th>操作</th>
      </tr>
    </thead>
    <tbody id="shopTbody"></tbody>
  </table>
  <div class="pagination">
    <button class="btn" onclick="prevPage()">上一页</button>
    <span id="pageInfo" class="muted">第 1 页 / 共 1 页</span>
    <button class="btn" onclick="nextPage()">下一页</button>
  </div>

  <div class="section">
    <h3>新增店铺</h3>
    <form id="createForm" onsubmit="createShop(event)">
      <input name="name" placeholder="名称" required />
      <input name="brand" placeholder="品牌（如 kfc）" required />
      <input name="address" placeholder="地址（支持搜索与地图拾取）" />
      <input name="lng" placeholder="经度" type="number" step="0.000001" required />
      <input name="lat" placeholder="纬度" type="number" step="0.000001" required />
      <button class="btn primary" type="submit">提交新增</button>
      <div id="createErr" class="err"></div>
    </form>

    <div id="mapTools">
      <input id="searchInput" placeholder="搜索地点，例如 王府井" style="width: 40%; padding: 6px;" />
      <button class="btn" onclick="searchPlace()">搜索</button>
    </div>
    <div id="map"></div>
  </div>

  <div class="section">
    <h3>编辑店铺</h3>
    <form id="editForm" onsubmit="updateShop(event)">
      <input name="id" placeholder="ID" readonly />
      <input name="name" placeholder="名称" required />
      <input name="brand" placeholder="品牌（如 kfc）" required />
      <input name="address" placeholder="地址" />
      <input name="lng" placeholder="经度" type="number" step="0.000001" required />
      <input name="lat" placeholder="纬度" type="number" step="0.000001" required />
      <button class="btn primary" type="submit">提交编辑</button>
      <button class="btn" type="button" onclick="resetEdit()">清空编辑</button>
      <div id="editErr" class="err"></div>
    </form>
  </div>

<script>
const API = '/api/shops';
let allShops = [];
let page = 1;

function qs(id) { return document.getElementById(id); }
function val(form, name) { return form.querySelector(`[name="${name}"]`).value; }
function set(form, name, v) { form.querySelector(`[name="${name}"]`).value = v ?? ''; }

function filterList(list) {
  const brand = qs('brandFilter').value.trim().toLowerCase();
  const cityKw = qs('cityFilter').value.trim();
  return list.filter(s => {
    const okBrand = !brand || (s.brand && s.brand.toLowerCase() === brand);
    const okCity = !cityKw || (s.address && s.address.includes(cityKw));
    return okBrand && okCity;
  });
}

async function loadShops() {
  const brand = qs('brandFilter').value.trim();
  const url = brand ? `${API}?brand=${encodeURIComponent(brand)}` : API;
  const res = await fetch(url);
  allShops = await res.json();
  page = 1;
  renderTable();
}

function renderTable() {
  const list = filterList(allShops);
  const size = Number(qs('pageSize').value);
  const totalPages = Math.max(1, Math.ceil(list.length / size));
  if (page > totalPages) page = totalPages;
  const start = (page - 1) * size;
  const pageItems = list.slice(start, start + size);

  const tbody = qs('shopTbody');
  tbody.innerHTML = '';
  pageItems.forEach(s => {
    const tr = document.createElement('tr');
    tr.innerHTML = `
      <td>${s.id ?? ''}</td>
      <td>${s.name ?? ''}</td>
      <td>${s.brand ?? ''}</td>
      <td>${s.address ?? ''}</td>
      <td>${s.lng ?? ''}</td>
      <td>${s.lat ?? ''}</td>
      <td class="actions">
        <button class="btn" onclick='fillEdit(${JSON.stringify(s).replace(/'/g, "'")})'>编辑</button>
        <button class="btn" onclick="delShop(${s.id}, '${s.brand ?? ''}')">删除</button>
        <button class="btn" onclick="openNav(${s.lng}, ${s.lat}, '${(s.name||'').replace(/'/g,'')}')">导航</button>
      </td>
    `;
    tbody.appendChild(tr);
  });

  qs('pageInfo').innerText = `第 ${page} 页 / 共 ${totalPages} 页`;
}

function prevPage() { if (page > 1) { page--; renderTable(); } }
function nextPage() {
  const list = filterList(allShops);
  const size = Number(qs('pageSize').value);
  const totalPages = Math.max(1, Math.ceil(list.length / size));
  if (page < totalPages) { page++; renderTable(); }
}

function fillEdit(s) {
  const f = qs('editForm');
  set(f, 'id', s.id);
  set(f, 'name', s.name);
  set(f, 'brand', s.brand);
  set(f, 'address', s.address);
  set(f, 'lng', s.lng);
  set(f, 'lat', s.lat);
}

function resetEdit() {
  const f = qs('editForm');
  ['id','name','brand','address','lng','lat'].forEach(n => set(f, n, ''));
}

function validatePayload(p) {
  const errs = [];
  if (!p.name) errs.push('名称必填');
  if (!p.brand) errs.push('品牌必填');
  if (Number.isNaN(p.lng) || p.lng < -180 || p.lng > 180) errs.push('经度需在 -180~180');
  if (Number.isNaN(p.lat) || p.lat < -90 || p.lat > 90) errs.push('纬度需在 -90~90');
  return errs;
}

async function createShop(e) {
  e.preventDefault();
  const f = qs('createForm');
  const payload = {
    name: val(f, 'name').trim(),
    brand: val(f, 'brand').trim().toLowerCase(),
    address: val(f, 'address').trim(),
    lng: Number(val(f, 'lng')),
    lat: Number(val(f, 'lat'))
  };
  const errs = validatePayload(payload);
  qs('createErr').innerText = errs.join('；');
  if (errs.length) return;
  await fetch(API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
  f.reset();
  await loadShops();
}

async function updateShop(e) {
  e.preventDefault();
  const f = qs('editForm');
  const id = Number(val(f, 'id'));
  const payload = {
    name: val(f, 'name').trim(),
    brand: val(f, 'brand').trim().toLowerCase(),
    address: val(f, 'address').trim(),
    lng: Number(val(f, 'lng')),
    lat: Number(val(f, 'lat'))
  };
  const errs = validatePayload(payload);
  qs('editErr').innerText = errs.join('；');
  if (errs.length) return;
  await fetch(`${API}/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
  await loadShops();
}

async function delShop(id, brand) {
  if (!confirm('确认删除该店铺？')) return;
  const url = `${API}/${id}` + (brand ? `?brand=${encodeURIComponent(brand)}` : '');
  await fetch(url, { method: 'DELETE' });
  await loadShops();
}

function openNav(lng, lat, name) {
  const title = encodeURIComponent(name || '目的地');
  const url = `https://uri.amap.com/navigation?to=${lng},${lat},${title}&mode=car&policy=1`;
  window.open(url, '_blank');
}

// 地图拾取与搜索
let map, marker, placeSearch, geocoder;
function initMap() {
  const center = [116.397428, 39.90923];
  map = new AMap.Map('map', { zoom: 12, center });
  marker = new AMap.Marker({ position: center });
  map.add(marker);

  geocoder = new AMap.Geocoder();
  placeSearch = new AMap.PlaceSearch({ map });

  map.on('click', e => {
    const { lnglat } = e;
    marker.setPosition(lnglat);
    fillCreateLngLat(lnglat);
    reverseAddress(lnglat);
  });
}

function fillCreateLngLat(lnglat) {
  const f = qs('createForm');
  set(f, 'lng', lnglat.lng.toFixed(6));
  set(f, 'lat', lnglat.lat.toFixed(6));
}

function reverseAddress(lnglat) {
  geocoder.getAddress([lnglat.lng, lnglat.lat], (status, result) => {
    if (status === 'complete' && result.regeocode) {
      const addr = result.regeocode.formattedAddress || '';
      set(qs('createForm'), 'address', addr);
    }
  });
}

function searchPlace() {
  const kw = qs('searchInput').value.trim();
  if (!kw) return;
  placeSearch.search(kw, (status, result) => {
    if (status === 'complete' && result.poiList && result.poiList.pois && result.poiList.pois.length) {
      const p = result.poiList.pois[0];
      const lnglat = p.location;
      if (!lnglat) return;
      map.setCenter(lnglat);
      marker.setPosition(lnglat);
      fillCreateLngLat(lnglat);
      set(qs('createForm'), 'address', `${p.name} ${p.address || ''}`);
    }
  });
}

initMap();
// 初次加载
loadShops();
</script>
</body>
</html>